`timescale 1ns / 1ps

module memory_game_top(
	input clk_i,
	input rst_i,
	input start0_i,
    input start1_i,
    input start2_i,
    input start3_i,
    input [7:0] sw_i,

	output [7:0] high_bus_o,
	output [7:0] low_bus_o,
	output [7:0] digit_en_o
	);
	//////////////////////////////////////
	///////// INTERNAL VARIABLES /////////
	//////////////////////////////////////

	wire clk_1khz;			// 1kHz clock signal, from module DIVIDER
	
	wire [2:0] status;		// major status, from module STATUS
	wire [2:0] substatus;	// minor status, used as showing random number, from module STATUS
	
	reg [1:0] success;		// [1]: input address valid; [0]: input number matches
	
	wire [14:0] rnd_num [4:0];			// 5 15-bit-sized random numbers, from module RANDOM
	reg [31:0] rnd_timestamp = 'd0;		// timestamp, used as random seed generator, updated in this module at 100MHz speed
	reg [31:0] rnd_seed;				// random seed: equals 
	
	reg [2:0] input_address;						// storing the input_address player has chosen
	(*DONT_TOUCH*) reg [14:0] input_number = 15'b0;	// storing the latest input
	
	reg [7:0] display_en;		// display enable
	reg [23:0] display_num;		// the number of character (octal, extended) to be displayed
	reg [7:0] display_extra;	// switches between the octal charset and the extended charset
	reg [2:0] display_blink;	// controls the blink, 1xx->4Hz, 01x->2Hz, 001->1Hz, others->no blink

	///////////////////////////
	///////// MODULES /////////
	///////////////////////////

	// 1kHz clock resource
	divider_1khz DIVIDER(
		clk_i,
		clk_1khz
	);
	
	// random number generator and register
	rnd_generator_register RANDOM(
		rnd_seed,
		rnd_num[0],
		rnd_num[1],
		rnd_num[2],
		rnd_num[3],
		rnd_num[4]
	);
	
	// status machine
	status_machine STATUS(
		clk_1khz,
		rst_i,
		start0_i,
		start1_i,
		start2_i,
		success,

		status,
		substatus
	);
	
	// convert display number to segment control signal
	display_driver DISPLAY(
		clk_1khz,
		display_en,
		display_num,
		display_extra,
		display_blink,

		high_bus_o,
		low_bus_o,
		digit_en_o
	);

	/////////////////////////////
	///////// FUNCTIONS /////////
	/////////////////////////////

	// timer
	always @(posedge clk_i) begin rnd_timestamp <= rnd_timestamp + 'd1; end
	
	// update display according to status machine
	always @(posedge clk_i) begin
		case (status)
			// WAIT FOR PLAYER
			3'd0: begin
				// show 8 zeros
				display_en <= 'hff;
				display_num <= 'o0000_0000;
				display_extra <= 'h00;
				display_blink <= 'h00;
				success <= 'd0;
			end
			// GENERATE AND SHOW NUMBER
			3'd1: begin
				display_extra <= 'h00;
			    display_en <= 'b0001_1111;
			    display_blink <= 'h00;
				case (substatus)
				    'd7: rnd_seed <= rnd_timestamp;
					'd0: display_num <= {9'b0, rnd_num[0]};
					'd1: display_num <= {9'b0, rnd_num[1]};
					'd2: display_num <= {9'b0, rnd_num[2]};
					'd3: display_num <= {9'b0, rnd_num[3]};
					'd4: display_num <= {9'b0, rnd_num[4]};
					default: display_en <= 'h00;
				endcase
			end
			'd2: begin
			    // PrESS-GO
			    display_en <= 'b1111_1111;
			    display_num <= 24'o2345_5050;
				display_extra <= 'b1110_0110;
				display_blink <= 'b010;
			end
			// INPUT ADDRESS
			'd3: begin
				input_address = (sw_i[2]==1) ? 'd4 : sw_i[2:0];
				if (sw_i>'d4) begin
				    display_en <= 'b1111_1111;
					display_num <= 24'o1111_1111;
					display_extra <= 'b1111_1111;
					display_blink <= 'b010;
					success[1] <= 'b0;
				end else begin
				    display_en <= 'b1111_1111;
					display_num <= {21'b0, input_address};
					display_extra <= 'b0000_0000;
					display_blink <= 'b000;
					success[1] <= 'b1;
				end
			end
			// INPUT NUMBER
			'd4: begin
			    display_en <= 'b1111_1111;
			    display_num <= 24'o0000_0000;
			    display_extra <= 'b0000_0000;
			    display_blink <= 'b100;
				if (start3_i) begin
				    // display_num = {9'b0, input_number};
				    case (input_address)
				        'd0: success[0] <= (input_number==rnd_num[0]) ? 1'b1 : 1'b0;
				        'd1: success[0] <= (input_number==rnd_num[1]) ? 1'b1 : 1'b0;
				        'd2: success[0] <= (input_number==rnd_num[2]) ? 1'b1 : 1'b0;
				        'd3: success[0] <= (input_number==rnd_num[3]) ? 1'b1 : 1'b0;
				        'd4: success[0] <= (input_number==rnd_num[4]) ? 1'b1 : 1'b0;
				    endcase
				end
			end
			'd5: begin
			    display_num <= {3'o0, input_address, 3'o0, rnd_num[input_address]};
			    display_en <= 8'b0111_1111;
			    display_extra <= 8'b0010_0000;
			    display_blink <= 3'b000;
			end 
		endcase
	end
	
	// get input_number while start_3 is pressed
	always @(posedge start3_i) begin
	    if (status=='d0) input_number[14:0] <= 'b0;
		else input_number[14:0] <= {input_number[11:0], sw_i[2:0]};
	end
endmodule
